package com.yangcangclub.pay.alipay;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConstants;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.internal.util.StringUtils;
import com.alipay.api.request.*;
import com.alipay.api.response.AlipayTradeCloseResponse;
import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.google.common.collect.Maps;
import com.yangcangclub.commons.config.SysConfig;
import com.yangcangclub.commons.exception.ApolloException;
import com.yangcangclub.commons.result.MessageEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

@Component
public class AlipayUtil {

    @Autowired
    SysConfig sysConfig;

    private static Logger logger = LoggerFactory.getLogger(AlipayUtil.class);

    private static AlipayClient alipayClient = null;

    public AlipayClient getAlipayClient() {
        if (alipayClient == null) {
            synchronized (AlipayUtil.class) {
                if (null == alipayClient) {
                    alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", sysConfig.getAppId(),
                            sysConfig.getPrivateKey(), AlipayConstants.FORMAT_JSON, AlipayConstants.CHARSET_UTF8,
                            sysConfig.getAliPublicKey(),AlipayConstants.SIGN_TYPE_RSA);
                }
            }
        }
        return alipayClient;
    }

    /** 支付宝app支付（传统方法）
     * @param tradeNo
     * @param amount
     * @param title
     * @param bodyInfo
     * @param reqNo
     * @return
     * @throws AlipayApiException
     * @throws UnsupportedEncodingException
     */
    public String getAlipayParameter(String tradeNo, String amount, String title, String bodyInfo,String reqNo) throws AlipayApiException, UnsupportedEncodingException {
        Map<String, String> parameterMap = new HashMap<String, String>();
        parameterMap.put("app_id", sysConfig.getAppId());
        parameterMap.put("method", "alipay.trade.app.pay");
//        parameterMap.put("format", AlipayConstants.FORMAT_JSON);
        parameterMap.put("charset", sysConfig.getCharset());
        Long timestamp = System.currentTimeMillis();
        DateFormat df = new SimpleDateFormat(AlipayConstants.DATE_TIME_FORMAT);
        df.setTimeZone(TimeZone.getTimeZone(AlipayConstants.DATE_TIMEZONE));
        parameterMap.put("timestamp", df.format(new Date(timestamp)));
        parameterMap.put("version", "1.0");
        parameterMap.put("notify_url", sysConfig.getYccBaseUrl()+sysConfig.getNotifyUrl()); // 支付宝服务器主动通知商户服务
        parameterMap.put("sign_type", AlipayConstants.SIGN_TYPE_RSA);
        Map<String, Object> bizContentMap = new LinkedHashMap<>();
        // 支付业务请求参数
        bizContentMap.put("body", bodyInfo);// 对交易或商品的描述
        bizContentMap.put("subject", title); // 订单标题
        bizContentMap.put("out_trade_no", tradeNo); // 商户订单号
        bizContentMap.put("timeout_express","60m");
        bizContentMap.put("total_amount", String.valueOf(amount));// 交易金额
        bizContentMap.put("product_code", "QUICK_MSECURITY_PAY");// 销售产品码
        bizContentMap.put("passback_params",URLEncoder.encode(reqNo, sysConfig.getCharset()));

        parameterMap.put("biz_content", JSON.toJSONString(bizContentMap)); // 业务请求参数  不需要对json字符串转义
        Map<String, String> signMap = AlipayUtil.paraFilter(parameterMap);
        String linkData = AlipaySignature.getSignContent(parameterMap);
        String sign = AlipaySignature.rsaSign(linkData, sysConfig.getPrivateKey(), AlipayConstants.CHARSET_UTF8, AlipayConstants.SIGN_TYPE_RSA);
        String payUrl = createLinkEncodedURL(parameterMap)+"&sign="+URLEncoder.encode(sign, sysConfig.getCharset());
        return payUrl.replace("+", "%20");
    }

    /**支付宝web支付（传统方法）
     * @param tradeNo
     * @param title
     * @param bodyInfo
     * @param reqNo
     * @return
     * @throws AlipayApiException
     * @throws UnsupportedEncodingException
     */
    public String getAlipayWebParameter(String tradeNo, String amount, String title, String bodyInfo,String reqNo) throws AlipayApiException, UnsupportedEncodingException {
        Map<String, String> parameterMap = new HashMap<String, String>();
        parameterMap.put("app_id", sysConfig.getAppId());
        parameterMap.put("method", "alipay.trade.wap.pay");
        parameterMap.put("charset", sysConfig.getCharset());
        parameterMap.put("return_url",sysConfig.getYccBaseUrl()+sysConfig.getAlipayWebReturnUrl());
        Long timestamp = System.currentTimeMillis();
        DateFormat df = new SimpleDateFormat(AlipayConstants.DATE_TIME_FORMAT);
        df.setTimeZone(TimeZone.getTimeZone(AlipayConstants.DATE_TIMEZONE));
        parameterMap.put("timestamp", df.format(new Date(timestamp)));
        parameterMap.put("version", "1.0");
        parameterMap.put("notify_url", sysConfig.getYccBaseUrl()+sysConfig.getNotifyUrl()); // 支付宝服务器主动通知商户服务
        parameterMap.put("sign_type", AlipayConstants.SIGN_TYPE_RSA);
        Map<String, Object> bizContentMap = new LinkedHashMap<>();
        // 支付业务请求参数
        bizContentMap.put("body", bodyInfo);// 对交易或商品的描述
        bizContentMap.put("subject", bodyInfo); // 订单标题
//        bizContentMap.put("subject", title); // 订单标题
        bizContentMap.put("out_trade_no", tradeNo); // 商户订单号
//        bizContentMap.put("timeout_express","90m");
        bizContentMap.put("total_amount", String.valueOf(amount));// 交易金额
        bizContentMap.put("product_code", "QUICK_MSECURITY_PAY");// 销售产品码
        bizContentMap.put("passback_params",URLEncoder.encode(reqNo, sysConfig.getCharset()));

        parameterMap.put("biz_content", JSON.toJSONString(bizContentMap)); // 业务请求参数  不需要对json字符串转义
        Map<String, String> signMap = AlipayUtil.paraFilter(parameterMap);
        String linkData = AlipaySignature.getSignContent(parameterMap);
        String sign = AlipaySignature.rsaSign(linkData, sysConfig.getPrivateKey(), AlipayConstants.CHARSET_UTF8, AlipayConstants.SIGN_TYPE_RSA);
        String payUrl = createLinkEncodedURL(parameterMap)+"&sign="+URLEncoder.encode(sign, sysConfig.getCharset());
        return payUrl.replace("+", "%20");
    }
    /** 
     * 除去数组中的空值和签名参数
     * @param sArray 签名参数组
     * @return 去掉空值与签名参数后的新签名参数组
     */
    public static Map<String, String> paraFilter(Map<String, String> sArray) {
        Map<String, String> result = new HashMap<String, String>();
        if (sArray == null || sArray.size() <= 0) {
            return result;
        }
        for (String key : sArray.keySet()) {
            String value = sArray.get(key);
            if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
                ) {
                continue;
            }
            result.put(key, value);
        }
        return result;
    }

    /** 
     * 把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串
     * @param params 需要排序并参与字符拼接的参数组
     * @return 拼接后字符串
     */
    public static String createLinkString(Map<String, String> params) {
        StringBuffer content = new StringBuffer();
        ArrayList keys = new ArrayList(params.keySet());
        Collections.sort(keys);
        int index = 0;
        for(int i = 0; i < keys.size(); ++i) {
            String key = (String)keys.get(i);
            String value = (String)params.get(key);
            if(StringUtils.areNotEmpty(new String[]{key, value})) {
                content.append((index == 0?"":"&") + key + "=" + value);
                ++index;
            }
        }
        return content.toString();
    }

    /**把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串，对value进行encode
     * @param params 需要排序并参与字符拼接的参数组
     * @return  拼接后字符串
     * @throws UnsupportedEncodingException
     */
    public String createLinkEncodedURL(Map<String, String> params) throws UnsupportedEncodingException{
        StringBuffer content = new StringBuffer();
        ArrayList keys = new ArrayList(params.keySet());
        Collections.sort(keys);
        int index = 0;
        for(int i = 0; i < keys.size(); ++i) {
            String key = (String)keys.get(i);
            String value = (String)params.get(key);
            if(StringUtils.areNotEmpty(new String[]{key, value})) {
                content.append((index == 0 ? "" : "&") + key + "=" + URLEncoder.encode(value, sysConfig.getCharset()));

                ++index;
            }
        }
        return content.toString();
    }

    /**支付宝验证
     * @param params
     * @return
     */
    public boolean verify(Map<String,String> params) {
        try {
            return AlipaySignature.rsaCheckV1(params, sysConfig.getAliPublicKey(), sysConfig.getCharset());
        } catch (AlipayApiException e) {
            e.printStackTrace();
            return false;
        }
    }


    /**支付宝查询订单
     * @param tradeNo
     * @return
     * @throws AlipayApiException
     */
    public Map<String,String> query(String tradeNo) throws AlipayApiException{
        alipayClient = getAlipayClient();
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        request.setBizContent("{\"out_trade_no\":\""+tradeNo+"\"}");
        AlipayTradeQueryResponse response = alipayClient.execute(request);
        Map map = Maps.newHashMap();
        if(response.isSuccess()){
            map.put("result",true);
            map.put("out_trade_no",response.getOutTradeNo());
            map.put("trade_no",response.getTradeNo());
            map.put("buyer_logon_id",response.getBuyerLogonId());
            map.put("trade_status",response.getTradeStatus());//交易状态：WAIT_BUYER_PAY（交易创建，等待买家付款）、TRADE_CLOSED（未付款交易超时关闭，或支付完成后全额退款）、TRADE_SUCCESS（交易支付成功）、TRADE_FINISHED（交易结束，不可退款）
            map.put("total_amount",response.getTotalAmount());
            map.put("invoice_amount",response.getInvoiceAmount());
            map.put("send_pay_date",response.getSendPayDate());
        } else {
            map.put("result",false);
            map.put("errorMsg","支付宝-订单查询-订单号:"+tradeNo+
                    "CODE:"+response.getCode()+
                    "MSG:"+response.getMsg()+
                    "SUB_CODE:"+response.getSubCode()+
                    "SUB_MSG:"+response.getSubMsg());
        }
        return map;
    }


    /** 统一收单交易退款接口
     * @param tradeNo
     * @param refundPrice
     * @param refundReason
     * @param outRequestNo
     * @return
     * @throws AlipayApiException
     */
    public Map<String,String> refund(String tradeNo,String refundPrice,String refundReason,String outRequestNo) throws AlipayApiException{
        alipayClient = getAlipayClient();
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        request.setBizContent("{" +
                "    \"out_trade_no\":\""+tradeNo+"\"," +
                "    \"refund_amount\":\""+refundPrice+"\"," +
                "    \"refund_reason\":\""+refundReason+"\"," +
                "    \"out_request_no\":\""+outRequestNo+"\"" +
                "  }");
        System.out.println(request.getBizContent().toString());
        AlipayTradeRefundResponse response = alipayClient.execute(request);
        Map map = Maps.newHashMap();
        if(response.isSuccess()){
            map.put("result",true);
            map.put("out_request_no",outRequestNo);
            map.put("out_trade_no",response.getOutTradeNo());
            map.put("trade_no",response.getTradeNo());
            map.put("refund_fee",response.getRefundFee());
            map.put("gmt_refund_pay",response.getGmtRefundPay());
            map.put("buyer_logon_id",response.getBuyerLogonId());
            map.put("fund_change",response.getFundChange());//本次退款是否发生了资金变化
        } else {
            map.put("result",false);
            map.put("errorMsg","支付宝-订单退款-订单号:"+tradeNo+
                    "CODE:"+response.getCode()+
                    "MSG:"+response.getMsg()+
                    "SUB_CODE:"+response.getSubCode()+
                    "SUB_MSG:"+response.getSubMsg());
        }
        return map;
    }

    /**统一收单交易退款查询
     * @param tradeNo
     * @param outRequestNo
     * @return
     * @throws AlipayApiException
     */
    public Map<String,String> refundQuery(String tradeNo ,String outRequestNo) throws AlipayApiException{
        alipayClient = getAlipayClient();
        AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
        request.setBizContent("{" +
                "    \"out_trade_no\":\""+tradeNo+"\"," +
                "    \"out_request_no\":\""+outRequestNo+"\"" +
                "  }");
        AlipayTradeFastpayRefundQueryResponse response = alipayClient.execute(request);
        Map map = Maps.newHashMap();
        if(response.isSuccess()){
            map.put("result",true);
            map.put("out_trade_no",response.getOutTradeNo());
            map.put("trade_no",response.getTradeNo());
            map.put("out_request_no",response.getOutRequestNo());
            map.put("total_amount",response.getTotalAmount());
            map.put("refund_amount",response.getRefundAmount());
            map.put("refund_reason",response.getRefundReason());
        } else {
            map.put("result",false);
            map.put("errorMsg","支付宝-订单退款查询-订单号:"+tradeNo+
                    "CODE:"+response.getCode()+
                    "MSG:"+response.getMsg()+
                    "SUB_CODE:"+response.getSubCode()+
                    "SUB_MSG:"+response.getSubMsg());
        }
        return map;
    }

    /**统一收单交易关闭接口
     * @param tradeNo
     * @return
     * @throws AlipayApiException
     */
    public Map<String,String> close(String tradeNo) throws AlipayApiException{
        alipayClient = getAlipayClient();
        AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
        request.setBizContent("{" +
                "    \"out_trade_no\":\""+tradeNo+"\"" +
                "  }");
        AlipayTradeCloseResponse response = alipayClient.execute(request);
        Map map = Maps.newHashMap();
        if(response.isSuccess()){
            map.put("result",true);
        } else {
            map.put("result",false);
            map.put("errorMsg","支付宝-订单交易关闭-订单号:"+tradeNo+
                    "CODE:"+response.getCode()+
                    "MSG:"+response.getMsg()+
                    "SUB_CODE:"+response.getSubCode()+
                    "SUB_MSG:"+response.getSubMsg());
        }
        return map;
    }

    /** 支付宝支付pay
     * @param tradeNo
     * @param amount
     * @param title
     * @param bodyInfo
     * @param reqNo
     * @param httpRequest
     * @param httpResponse
     * @throws AlipayApiException
     * @throws ServletException
     * @throws IOException
     */
    public void pay(String tradeNo, String amount, String title, String bodyInfo, String reqNo,
                    HttpServletRequest httpRequest, HttpServletResponse httpResponse)
            throws AlipayApiException , ServletException, IOException {
        alipayClient = getAlipayClient();
        AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
        request.setReturnUrl(sysConfig.getYccBaseUrl()+sysConfig.getAlipayWebReturnUrl());
        request.setNotifyUrl(sysConfig.getYccBaseUrl()+sysConfig.getNotifyUrl());
        request.setBizContent("{" +
                "    \"out_trade_no\":\""+tradeNo+"\"," +
                "    \"total_amount\":"+amount+"," +
                "    \"subject\":\""+title+"\"," +
                "    \"body\":\""+bodyInfo+"\"," +
                "    \"passback_params\":\""+reqNo+"\"," +
                "    \"product_code\":\"QUICK_WAP_PAY\"" +
                "  }");
        String form = alipayClient.pageExecute(request).getBody(); //调用SDK生成表单
        httpResponse.setContentType("text/html;charset=" + AlipayConstants.CHARSET_UTF8);
        httpResponse.getWriter().write(form);//直接将完整的表单html输出到页面
        httpResponse.getWriter().flush();
    }


}
